package com.xueqiu.movieplaymanagersystem.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.xueqiu.movieplaymanagersystem.entity.Comment;
import com.xueqiu.movieplaymanagersystem.mapper.CommentMapper;
import com.xueqiu.movieplaymanagersystem.service.CommentService;
import com.xueqiu.movieplaymanagersystem.utils.UUIDUtils;
import com.xueqiu.movieplaymanagersystem.utils.sensitiveUtils.SensitiveFilter;

import java.util.*;

/**
 * @author xueqiu
 */
@Service
@Slf4j
public class CommentServiceImpl implements CommentService {

    /**
     * “逻辑结构生成中的”所有楼中楼评论列表；可复用
     * 逻辑结构填入元素的顺序，是树形结构的“中左右”顺序
     * 树形结构的每一个节点下的孩子的顺序，从左到右是按照“评论创建按时间顺序升序”排序
     */
    private List<Comment> tempCommentStruct = new ArrayList<>();

    @Autowired
    private CommentMapper commentMapper;

    @Autowired
    private SensitiveFilter sensitiveFilter;

    /**
     * 新增
     *
     * @param originData 数据源
     * @return 新增是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(Comment originData) {
        //id是使用UUID生成而不是自增
        originData.setId(UUIDUtils.uuid());
        originData.setCreateTime(new Date());

        String content = originData.getContent();
        //过滤工具类对正文内容过滤
        String filterString = sensitiveFilter.filter(content, '*');
        /*
        只要过滤后的结果不再包含原串；那就是相当于执行了违禁词替换！
        为什么不判断过滤后的结果是否包含*？
        因为用户可能自己把*写进正文
         */
        if (!filterString.equals(content)) {
            originData.setContent("此评论包含违禁词，所以强制删除！");
        }
        int insert = commentMapper.insert(originData);
        log.info("CommentServiceImpl save originData ----> {}",originData);
        return insert == 1;
    }

    /**
     * 查询；目标新闻下的所有评论；同时完善评论逻辑层级结构
     *
     * @param blogId 查看评论的新闻id
     * @return 主楼层评论列表；可能为EMPTY
     */
    @Override
    @Transactional(readOnly = true)
    public List<Comment> findByBlogId(String blogId) {
        return findByBlogIdAndParentId("-1", blogId);
    }

    /**
     * 查询；目标新闻下的指定主楼层的所有评论；同时完善评论逻辑层级结构
     * 主楼层的“主”是相对而言；存在绝对的“主”，也就是parentId = -1的楼层
     * 构建逻辑结构运用递归，此方法的性能开销很大
     *
     * @param parentId 当此值 = -1；代表“指定主楼层”是顶级主楼层
     * @param blogId   目标新闻
     * @return 所有主楼层评论
     */
    @Override
    @Transactional(readOnly = true)
    public List<Comment> findByBlogIdAndParentId(String parentId, String blogId) {
        //查询出所有主楼层评论
        List<Comment> mainComments = commentMapper.selectByBlogIdAndParentId(parentId, blogId);
        //查询所有楼中楼评论
        List<Comment> childrenComments = commentMapper.selectByBlogId(blogId);

        for (Comment mainLayer : mainComments) {
            String mainLayerId = mainLayer.getId();
            String mainLayerNickname = mainLayer.getNickname();
            //从所有楼中楼评论中，筛选属于当前主楼层的楼中楼评论（第一级）
            List<Comment> childCommentsOfMainLayer = findByParentIdNotNull(mainLayerId, childrenComments);
            //对属于当前主楼层的楼中楼评论，进行父子关系绑定
            combineRelationship(childCommentsOfMainLayer, mainLayerNickname, childrenComments);
            //设置当前主楼层，“逻辑结构生成完毕的”所有楼中楼评论；
            mainLayer.setReplyComments(tempCommentStruct);
            //清空临时列表
            tempCommentStruct = new ArrayList<>();
        }
        log.info("CommentServiceImpl findByBlogIdAndParentId mainComments ----> {}",mainComments);
        return mainComments;
    }

    /**
     * 从所有楼中楼评论中，筛选属于当前主楼层的楼中楼评论
     *
     * @param mainLayerId 主楼层id
     * @param children    所有楼中楼评论集合
     * @return 属于当前主楼层的楼中楼评论列表
     */
    private List<Comment> findByParentIdNotNull(String mainLayerId, List<Comment> children) {
        //声明当前主楼层拥有的楼中楼评论集合；使用hashSet运用去重特性
        Set<Comment> childrenCommentsOfCurrentMainLayer = new HashSet<>();
        ListIterator<Comment> listIterator = children.listIterator();
        //逐个判断每一个楼中楼评论的主楼层id是否为mainLayerId
        while (listIterator.hasNext()) {
            Comment next = listIterator.next();
            if (mainLayerId.equals(next.getParentCommentId())) {
                //如果是，那么把此楼中楼评论添加进集合
                childrenCommentsOfCurrentMainLayer.add(next);
                //迭代器中删除当前楼中楼评论
                listIterator.remove();
            }
        }
        return new ArrayList<>(childrenCommentsOfCurrentMainLayer);
    }

    /**
     * 对属于当前主楼层的楼中楼评论，进行父子关系绑定
     * 对已经筛选出来的，当前主楼层下的所有楼中楼评论
     * 每一个楼中楼评论A，都去寻找childCommentsOfMainLayer中的属于A的“楼中楼评论”
     *
     * @param childCommentsOfMainLayer 从所有楼中楼评论中，筛选属于当前主楼层的楼中楼评论
     * @param mainLayerNickname        主楼层评论的所有者的昵称
     * @param children                 所有楼中楼评论；必须带上不能省略
     */
    private void combineRelationship(List<Comment> childCommentsOfMainLayer, String mainLayerNickname, List<Comment> children) {
        //健壮性判断；是否存在楼中楼评论
        if (!childCommentsOfMainLayer.isEmpty()) {
            //这个循环是将第一级的楼中楼评论进行“第一级与第二级间的父子关系绑定”
            for (Comment childComment : childCommentsOfMainLayer) {
                String childId = childComment.getId();
                String childrenNickname = childComment.getNickname();
                //设置当前楼中楼评论的所有者的昵称
                childComment.setParentNickname(mainLayerNickname);
                //填入“逻辑结构生成中的”所有楼中楼评论列表
                tempCommentStruct.add(childComment);

                //递归查询比第一级更下一级的
                combineRelationshipRecursively(childId, childrenNickname, children);
            }
        }
    }

    /**
     * 递归查询和绑定父子关系
     *
     * @param parentId       父评论id
     * @param parentNickname 父评论昵称
     * @param children       所有楼中楼评论
     */
    private void combineRelationshipRecursively(String parentId, String parentNickname, List<Comment> children) {
        /*
        因为父评论id是添加时填入的，不具有传递性；
        所以每个评论的所有子评论，都需要findByParentIdNotNull()查询
        TODO 这里可以使用数据结构或者数据库重构来使性能进一步优化
         */
        List<Comment> childCommentsOfCurrentParent = findByParentIdNotNull(parentId, children);

        if (!childCommentsOfCurrentParent.isEmpty()) {
            for (Comment childrenComment : childCommentsOfCurrentParent) {
                String childrenId = childrenComment.getId();
                String childrenNickname = childrenComment.getNickname();
                //设置当前评论的所有者的昵称
                childrenComment.setParentNickname(parentNickname);
                //填入“逻辑结构生成中的”所有楼中楼评论列表
                tempCommentStruct.add(childrenComment);
                //递归
                combineRelationshipRecursively(childrenId, childrenNickname, children);
            }
        }
    }

    /**
     * 查询所有评论条数根据新闻id
     *
     * @param blogId 新闻id
     * @return 所有评论条数
     */
    @Override
    @Transactional(readOnly = true)
    public Integer findCountByBlogId(String blogId) {
        Integer count = commentMapper.selectCountByBlogId(blogId);
        log.info("CommentServiceImpl findCountByBlogId count ----> {}",count);
        return count;
    }

    /**
     * 查询数据库中所有评论条数
     *
     * @return 所有评论条数
     */
    @Override
    @Transactional(readOnly = true)
    public Integer findCount() {
        Integer count = commentMapper.selectCount();
        log.info("CommentServiceImpl findCount count ----> {}",count);
        return count;
    }
}
